﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

#include <map>
#include <vector>
#include <string>
#include <math.h>

#ifdef _MSC_VER
# define DLL_EXPORT __declspec(dllexport)
# define DLL_IMPORT __declspec(dllimport)
#else
# define DLL_EXPORT
# define DLL_IMPORT
#endif

#ifdef EXPRENGINE_EXPORTS
#define EXPR_UNITMAP_API DLL_EXPORT
#else
#define EXPR_UNITMAP_API DLL_IMPORT
#endif

namespace gcmp
{
    const double PRECISION_DEF = 0.00001;

    /// \brief 表达式单位类型
    ///
    /// 1. 赋值顺序不能变动，逻辑按照顺序进行判断处理，为的是之后方便单位扩展
    /// 2. 序号从Num到Double之间添加自然单位，是可进行计算的
    enum class ExprUnitType : uint32_t
    {
        Invalid = 0U,

        // 能计算的从01开始
        Num = 1U, ///< 数据，对应界面输入的数据，如：1.3、10
        Length = 2U,
        Angle = 3U,
        Time = 4U,

        // 如果还需要添加，从此处开始

        Int = (1U << 16) - 2, ///< 整数
        Double = (1U << 16) - 1, ///< 数值

        // 不能计算的从Bool开始
        Bool = 1U << 16,
        String = (1U << 16) + 1
    };

    /// \brief 表达式单位容器类
    class EXPR_UNITMAP_API UnitMap
    {
    public:
        /// \brief 获取错误字符串
        /// 
        /// \return 错误字符串
        std::wstring GetErrorStr() const;

        /// \brief 设置错误字符串
        /// 
        /// \param val 错误字符串
        void SetErrorStr(const std::wstring& val);

        /// \brief 构造函数默认初始化为Num
        UnitMap();

        /// \brief 默认析构函数
        ~UnitMap();

        /// \brief 构造函数
        /// 
        /// \param type 表达式单位类型
        explicit UnitMap(const ExprUnitType& type);

        /// \brief 拷贝构造函数
        /// 
        /// \param types 源对象
        UnitMap(const UnitMap& types);

        /// \brief 拷贝赋值运算符
        /// 
        /// \param[in] types 源对象
        UnitMap& operator=(const UnitMap& types);

        /// \brief 初始化
        /// 
        /// \param[in] type 表达式单位类型
        /// \param[in] value 对应的值
        /// \return 成功返回true，失败返回false
        bool InitialFrom(const ExprUnitType& type, const double& value = 1);

        /// \brief 判断是否有效的单位
        /// 
        /// \return 成功返回true，失败返回false
        bool IsValid() const;

        /// \brief 判断单位容器是否为空
        ///
        /// \return 是返回true，否返回false
        bool IsEmpty() const;

        /// \brief 判断是否数据
        /// 
        /// \return 是返回true，否返回false
        bool IsNum() const;

        /// \brief 判断是否自然单位
        /// 
        /// \return 是返回true，否返回false
        bool IsNaturalUnit() const;

        /// \brief 判断是否含有某单位类型
        /// 
        /// \param[in] type 表达式单位类型
        /// \return 有返回true，没有返回false
        bool MapHasType(const ExprUnitType& type);

        /// \brief 判断是否数据或自然单位
        /// 
        /// \return 是返回true，否返回false
        bool IsNumOrNatureUnit() const;

        /// \brief 判断给定单位类型是否可计算
        /// 
        /// \param[in] type 表达式单位类型
        /// \return 是返回true，否返回false
        static bool IsCalculatable(const ExprUnitType& type);

        /// \brief 判断给定单位类型是否自然单位
        /// 
        /// \param[in] type 表达式单位类型
        /// \return 是返回true，否返回false
        static bool IsNaturalUnit(const ExprUnitType& type);

        /// \brief 获得单一单位
        /// 
        /// \return 如果是单一单位，则返回单位；否则返回Invalid
        ExprUnitType GetSingleUnit() const;

        /// \brief 清空
        void Clear();

        /// \brief 拷贝自另一个对象
        /// 
        /// \param[in] typeMap 源对象
        void GetCopyFrom(const UnitMap& typeMap);

        /// \brief 判断与给定对象是否相同
        /// 
        /// \param[in] typeMap 源对象
        /// \return 相同返回true，不相同返回false
        bool UnitIsSame(const UnitMap& typeMap) const;

        /// \brief 获取某个单位类型的幂值
        /// 
        /// \param[in] type 表达式单位类型
        /// \return 单位幂值
        double GetUnitCount(const ExprUnitType& type) const;

        /// \brief 查找所有单位类型
        /// 
        /// \param[out] types 表达式单位类型向量
        void FindUnits(std::vector<ExprUnitType>& types) const;

        /// \brief 幂运算
        /// 
        /// \param[in] value 幂次
        void Pow(const double& value);

        /// \brief 乘以一个单位
        /// 
        /// \param[in] type 被乘单位类型
        /// \param[in] value 被乘单位幂值
        /// \return 成功返回true，失败返回false
        bool Multiply(const ExprUnitType& type, const double& value = 1);

        /// \brief 除以一个单位
        /// 
        /// \param[in] type 除数单位类型
        /// \param[in] value 除数单位幂值
        /// \return 成功返回true，失败返回false
        bool Divided(const ExprUnitType& type, const double& value = 1);

        /// \brief 乘以一个表达式单位容器
        /// 
        /// \param[in] typeMap 被乘表达式单位容器对象
        /// \return 成功返回true，失败返回false
        bool Multiply(const UnitMap& typeMap);

        /// \brief 除以一个表达式单位容器
        /// 
        /// \param[in] typeMap 除数表达式单位容器对象
        /// \return 成功返回true，失败返回false
        bool Divided(const UnitMap& typeMap);

    private:
        bool IsZero(const double& value)const;
        std::map<ExprUnitType, double> m_units;
        std::wstring m_errorStr;
    };

    /// \brief 单位助手类
    class EXPR_UNITMAP_API UnitHelper
    {
    public:
        /// \brief 单位容器对象求和，并检查是否成功
        /// 
        /// \param[in] left 被加数单位容器对象
        /// \param[in] right 加数单位容器对象
        /// \param[out] result 结果单位容器对象
        /// \return 成功返回true，失败返回false
        static bool CheckUnitAdd(const UnitMap& left, const UnitMap& right, UnitMap& result);

        /// \brief 单位容器对象求差，并检查是否成功
        /// 
        /// \param[in] left 被减数单位容器对象
        /// \param[in] right 减数单位容器对象
        /// \param[out] result 结果单位容器对象
        /// \return 成功返回true，失败返回false
        static bool CheckUnitSubtract(const UnitMap& left, const UnitMap& right, UnitMap& result);

        /// \brief 单位容器对象求积，并检查是否成功
        /// 
        /// \param[in] left 被乘数单位容器对象
        /// \param[in] right 乘数单位容器对象
        /// \param[out] result 结果单位容器对象
        /// \return 成功返回true，失败返回false
        static bool CheckUnitMultiply(const UnitMap& left, const UnitMap& right, UnitMap& result);

        /// \brief 单位容器对象求商，并检查是否成功
        /// 
        /// \param[in] left 被除数单位容器对象
        /// \param[in] right 除数单位容器对象
        /// \param[out] result 结果单位容器对象
        /// \return 成功返回true，失败返回false
        static bool CheckUnitDivided(const UnitMap& left, const UnitMap& right, UnitMap& result);

        /// \brief 单位容器对象取模（求余），并检查是否成功
        /// 
        /// \param[in] left 被除数单位容器对象
        /// \param[in] right 除数单位容器对象
        /// \param[out] result 结果单位容器对象
        /// \return 成功返回true，失败返回false
        static bool CheckUnitModulus(const UnitMap& left, const UnitMap& right, UnitMap& result);

        /// \brief 单位容器对象进行比较，并检查是否成功
        /// 
        /// \param[in] left 比较对象1：单位容器对象
        /// \param[in] right 比较对象2：单位容器对象
        /// \param[out] result 结果单位容器对象
        /// \return 成功返回true，失败返回false
        static bool CheckUnitCompare(const UnitMap& left, const UnitMap& right, UnitMap& result);
    };
}
